<?php

namespace process;


use App\Admin\Service\Setting\SettingCrontabService;
use Newbie\Crontab\NewbieCrontab;
use Newbie\Crontab\NewbieExecutor;
use Workerman\Connection\TcpConnection;
use Workerman\Worker;
use Workerman\Crontab\Crontab as BaseCrontab;

class Crontab

{
    const NORMAL_STATUS = 0;

    /**
     * @var SettingCrontabService
     */
    protected SettingCrontabService $settingCrontabService;

    protected static $pool = [];

    protected static $debug = false;

    public function onWorkerStart(Worker $worker)
    {
        $config = config('app.crontab');
        self::$debug = $config['debug'];
        $this->settingCrontabService = container()->get(SettingCrontabService::class);
        $this->crontabInit();
    }

    public function onMessage(TcpConnection $connection, $data)
    {
        $data = json_decode($data, true);
        $method = $data['method'];
        $args = $data['args'];
        $connection->send(call_user_func([$this, $method], $args));
    }

    /**
     * 当客户端与Workman建立连接时(TCP三次握手完成后)触发的回调函数
     * 每个连接只会触发一次onConnect回调
     * 此时客户端还没有发来任何数据
     * 由于udp是无连接的，所以当使用udp时不会触发onConnect回调，也不会触发onClose回调
     * @param TcpConnection $connection
     */
    public function onConnect(TcpConnection $connection)
    {
        //$this->checkCrontabTables();
    }


    /**
     * 初始化定时任务
     * @return void
     */
    private static function crontabInit(): void
    {
        $executor = container()->get(NewbieExecutor::class);
        $settingCrontabService = container()->get(settingCrontabService::class);
        $executor->debug = self::$debug;
        $list = $settingCrontabService->mapper->getModel()->where('status', self::NORMAL_STATUS)->get();
        foreach ($list as $data) {
            $crontab = self::getCrontabByModel($data);
            self::$pool[$crontab->getCrontabId()] = [
                'id' => $crontab->getCrontabId(),
                'crontab' => new BaseCrontab($crontab->getRule(), function () use ($crontab, $executor) {
                    $executor->execute($crontab);
                })
            ];
        }
    }

    public static function restartAll()
    {
        foreach (self::$pool as $k => $item) {
            $item['crontab']->destroy();
        }
        self::$pool = [];
        self::crontabInit();
    }

    /**
     * 按id销毁定时器
     * @param $id
     * @return void
     */
    public static function destroy($id)
    {
        if (isset(self::$pool[$id])) {
            self::$debug && self::writeProcessLn("定时器销毁" . $id, "Destroy");
            self::$pool[$id]['crontab']->destroy();
        }
    }

    public static function getCrontabByModel($data): NewbieCrontab
    {
        $crontab = new NewbieCrontab();
        $crontab->setCallback($data['parameter']);
        $crontab->setCrontabId($data['id']);
        $crontab->setType($data['type']);
        $crontab->setEnable(true);
        $crontab->setCrontabId($data['id']);
        $crontab->setName($data['name']);
        $crontab->setParameter($data['parameter'] ?: '');
        $crontab->setRule($data['rule']);
        $crontab->setTarget($data['target']);
        return $crontab;
    }

    /**
     * 输出日志
     * @param string $msg
     * @param bool $isSuccess
     */
    private static function writeln(string $msg, bool $isSuccess = false): void
    {
        echo '[' . date('Y-m-d H:i:s') . '] ' . $msg . ($isSuccess ? " [Ok] " : " [Fail] ") . PHP_EOL;
    }

    /**
     * 输出日志
     * @param string $msg
     * @param string $process
     */
    private static function writeProcessLn(string $msg, string $process): void
    {
        echo '[' . date('Y-m-d H:i:s') . '] ' . $msg . (" [$process] ") . PHP_EOL;
    }

}